[TOC]

# 1. 开始

项目由基于 Vue2 的 uni-app 迁移到了 Vue3 的 uni-app,遇到了主包大小的问题。

原因在于项目模式是“大组件,小页面”,且使用了较多的 npm 包,比如 Press Plus 等。Press Pluslocal-componentcomponent 子仓的位置都在分包以外,按照小程序的打包规则(基于目录位置),自然是放到主包的。当使用越来越多的外部组件时,必然会遇到主包过大的问题。

Vue2 时已经写了基于 Webpack 的组件分发和脚本分发插件,这次来实现 Vite 版本(Vue3)的组件分发插件,脚本分发插件后面看情况再开发。

# 2. 思路

把大象放进冰箱,需要3步:

  1. 打开冰箱门
  2. 把大象放进去
  3. 关闭冰箱门

组件分发比上面简单,需要2步:

  1. 找到需要分发的组件
  2. 分发

如何找到需要分发的组件:

  1. 获取依赖关系,可以通过遍历 bundlegenerateBundle 钩子中)实现
  2. 获取全局组件、主包、分包信息,可以通过读取 bundle['app.json'] 实现
  3. 根据依赖关系、分包列表等,分析数据,拿到需要分发的组件,以及要分发到哪里

如何分析数据:

  1. 依赖关系指的是“子子组件A => 子组件B => 页面C”这种关系
  2. 需要递归分析吗?必须需要。如果不递归,只能拿到第一层或者前几层的引用关系,无法得到使用一个组件的所有分包
  3. 分析时,首先能拿到的是“父引用子”,需要先递归,然后翻转,翻转就是让“子节点当作键”(理论上先翻转后递归也可以)
  4. 递归分析,只要没被主包页面引用,就是被分包使用了。前提是组件是被使用的。就是从子节点到根结点,根结点一定属于某个分包或主包的一个页面。

列一下符合规范的引用关系类型:

  1. 主包组件 => 主包页面(直接引用)
  2. 主包组件 => ...主包组件... => 主包页面(间接引用)
  3. 主包组件 => 分包页面(直接引用)
  4. 主包组件 => ...主包组件.... => 分包页面(间接引用)
  5. 主包组件 => ...分包组件... => 分包页面(间接引用)
  6. 主包组件 => ...主包组件... => ...分包组件... => 分包页面(间接引用)
  7. 分包组件 => 分包页面(直接引用)
  8. 分包组件 => ...分包组件... => 分包页面(间接引用)

其中类型 3、4、5、6 都是需要分发的。

分发步骤:

  1. 移动组件到目标位置
  2. 修改引用关系
  3. 删除原有组件

修改引用关系又分为:

  1. 修改之前引用者的引用关系,比如“子组件=>分包页面”中的分包页面
  2. 修改移动组件内部的引用关系,比如“子组件=>分包页面”中的子组件

第1步是精确替换,只替换对移动的组件的引用关系。第2步是全量替换,对所有引用关系进行替换。

第1步中,对于"主包组件A => 主包组件B => 分包页面C"这种关系,A被移动了,需要替换B中的引用关系吗?

其实是不需要的(第1步),如果A被移动了,说明B一定也被移动了,否则就会反向依赖,而这在数据分析阶段就已经规避了。

主包组件B既属于1,又属于2,也就是既是被移动的组件,又是其他移动组件的父组件。需要在第一步排除(不处理),并在第二步的时候额外判断,取"子子组件"移动后真正的路径。

# 3. 核心点

几个核心点:

  1. 递归分析引用关系,以及避免循环引用
  2. 找到需要派发的组件,以及目标位置
  3. 替换引用时,需要对移动组件内部的引用关系进行替换。这里用了正则去匹配,然后根据之前的组件位置、新的组件位置、之前的引用路径,生成新的引用路径。

# 4. 效果

在一个项目中使用了,并且配置了只有一个分包使用时才移动,主包能够减少 0.8MB,降低了 41.5%,并且由于只移动了最需要移动的,所以总包大小基本没有变化(变化的10K左右是由于生成的路径变深)。

如果把 limit 设置为0,也就是只要主包不使用,就移动到分包中,可以将主包减少到 1015KB,降低了 47%。但由于多个分包中存在重复组件,总包也会增加。

使用方可以根据自身项目需要,灵活设置此选项。